#include "Graphviz_Static_lyvm.h"
#include<assert.h>
#include<set>

#include "shellapi.h"	// 使用ShellExecute以執行dot.exe
#include <iostream>	// 使用 >>,  ex fin >> output.txt
#include <fstream>
using namespace std;

//using namespace Ogre;

// 使用外部的Graphviz進行DIVA_VM的排列
// DIVA -> input.dot -> Graphviz(dot.exe) -> output.dot -> do layout
void Graphviz_Static_lyvm::algorithm(){

	// 0. renew, 刪除先前的dot檔案
	remove("input.dot");
	remove("output.dot");

	// 1. 取得所有VMs
	vector<DIVA_VM* > vms = _area->getAllVMs();

	// 2. 取得所有VM之間的連結關係
	multimap<DIVA_VM*, DIVA_VM*> links = _area->getLinks();

	// 3. 建立dot文件 input.dot
	// format:
	// digraph G {
	//   node1 [shape="box", width=??, height=??];
	//   node2 [shape="box", width=??, height=??];
	//   node3 [shape="box", width=??, height=??];
	//   ...
	//   node1 -> node2 [shape="vee"];
	//   node2 -> node3 [shape="vee"];
	//   ...
	// }
	// 
	//
	// 將vm資訊記錄成dot格式
	string vmInfoInDOT = "digraph g {\n";

	// a. 先讀取VMs, 取得並記錄所有VM的大小 為dot格式
	int vm_count = 1;
	map<DIVA_VM*, string> vm_names;	// 為每個VM命名 ex. vm_node_1
	for(vector<DIVA_VM* >::iterator itr=vms.begin(); itr!=vms.end(); itr++)
	{
		// 為每個VM命名，以在dot中區別之
		// vm_names[DIVA_VM *] = name
		vm_names[*itr] = "vm_node_" + Ogre::StringConverter::toString(vm_count++);

		// vm_node_X [shape = "box", width=??, height=??];
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, "  ");
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, vm_names.find(*itr)->second.c_str());
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, " [shape=\"box\",width=");
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, Ogre::StringConverter::toString(*(*itr)->get_vm_sizex()).c_str());
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, ", height=");
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, Ogre::StringConverter::toString(*(*itr)->get_vm_sizey()).c_str());
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, "];\n");
	}

	// b. 再讀取links, 取得並記錄所有VM之間的連結 為dot格式
	for(multimap<DIVA_VM*, DIVA_VM*>::iterator itr=links.begin(); itr!=links.end(); itr++)
	{
		// vm_node_X -> vm_node_Y;
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, "  ");
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, vm_names.find(itr->first)->second.c_str());
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, " -> ");
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, vm_names.find(itr->second)->second.c_str());
		vmInfoInDOT.insert(vmInfoInDOT.length() - 1, ";\n");
	}

	vmInfoInDOT.insert(vmInfoInDOT.length() - 1, "}\n");


	// 4. 呼叫Graphviz  ,  前提是電腦必須裝載Graphviz, 必且將dot.exe的路徑加入到環境變數的PATH之中
	// format: dot input.dot
	// 如果可以直接抓輸出檔就比較簡單
	// 否則就使用 dot input.dot -o output.dot
	ofstream fout;
	// 如果檔案已經開啟，則先關閉之
	if (fout.is_open())
	{
		fout.close();
	}
	fout.open("input.dot");	// 將DIVA_VM的資訊轉換成DOT格式儲存之，開啟方式


	// 將DIVA_VM資訊儲存成dot檔案
	fout.write(vmInfoInDOT.c_str(), vmInfoInDOT.length());
	fout.close();
	
	// 呼叫Graphviz 將DIVA_VMs排列
	string rootDir = getenv("OGRE_HOME");
	rootDir += "\\bin\\debug";

	// 因為vista以後對於執行檔加入了權限控管，因此相關的程式碼也必須取得足夠的權限才能使用
	Ogre::String openMode = (WINVER >= 0x0600) ?"runas" : "open";	// 0x0600為vista版本代號

	// ShellExecute的參數說明
	//hwnd			視窗的名稱 (不知道這樣解釋對不對) 
	//lpOperation	進行的操作，如"open","print","explore"分別對應 "開啟","列印","瀏覽"， 也可以為空（""），此時表示進行預設的操作。 
	//lpFile		要操作的文件。 
	//lpParameters	如果lpFile指定的是一個可執行檔則表示參數  
	//lpDirectory	操作進行的目錄 
	//nShowCmd		新的應用程式的運行方式。其可用的值如下： 
	//  SW_HIDE				隱藏 
	//  SW_MAXIMIZE			最大化 
	//  SW_MINIMIZE			最小化，並把Z order順序在此視窗之後（即視窗下一層）的視窗啟動 
	//  SW_RESTORE			啟動視窗並還原為初始化大小 
	//  SW_SHOW				以當前大小和狀態啟動視窗 
	//  SW_SHOWDEFAULT		以預設方式運行 
	//  SW_SHOWMAXIMIZED	啟動視窗並最大化 
	//  SW_SHOWMINIMIZED	啟動視窗並最小化  
	//  SW_SHOWMINNOACTIVE	最小化但不改變當前啟動的視窗 
	//  SW_SHOWNA			以當前狀態顯示視窗但不改變當前啟動的視窗  
	//  SW_SHOWNOACTIVATE	以初始化大小顯示視窗但不改變當前啟動的視窗 
	//  SW_SHOWNORMAL		啟動並顯示視窗，如果是最大(小)化，視窗將會還原。第一次運行程式 時應該使用這個值
	//
	// 在執行檔路徑上，因為Win7上，即使獲取足夠的權限執行，但路徑上仍需使用絕對路徑來才執行，相對路徑會被視為找不到檔案...
	HINSTANCE dot = ShellExecute(NULL, openMode.c_str(), "dot.exe", "-Kneato input.dot -o output.dot" , rootDir.c_str(), SW_HIDE);
	WaitForSingleObject(dot,INFINITE);
	Sleep(500);	// 使用等待方式，等待output.dot完成
	// 5. 解析dot輸出內容，取得pos資訊並一一設定之
	// format:
	//digraph g {
	//	node [label="\N"];
	//	graph [bb="0,0,11649,9564.5"];
	//	vm_node_1 [shape=box, width=160, height=130, pos="5799,4753.2"];
	//	vm_node_2 [shape=box, width=160, height=130, pos="5815.7,4828.2"];
	//	vm_node_3 [shape=box, width=160, height=130, pos="5888.4,4855.7"];
	//	vm_node_4 [shape=box, width=160, height=130, pos="5761,4883.5"];
	//	vm_node_5 [shape=box, width=160, height=130, pos="5786.8,4681"];
	//	vm_node_1 -> vm_node_2 [pos="e,5815.7,4828.2 5815.7,4828.1 5815.7,4828.1 5815.7,4828.1 5815.7,4828.1"];
	//	vm_node_1 -> vm_node_5 [pos="e,5786.8,4681 5786.8,4681.1 5786.8,4681 5786.8,4681 5786.8,4681"];
	//	vm_node_2 -> vm_node_3 [pos="e,5888.4,4855.7 5888.4,4855.7 5888.4,4855.7 5888.4,4855.7 5888.4,4855.7"];
	//	vm_node_2 -> vm_node_4 [pos="e,5761,4883.4 5761.2,4883.3 5761.1,4883.3 5761.1,4883.3 5761.1,4883.4"];
	//}
	ifstream fin;
	fin.open("output.dot");
	
	// 找不到output.doc, 離開
	if(fin.fail())
	{
		return;
	}

	char str_a_line[256];	// 記錄每行的檔案內容
	string str_line, token;	// 暫存每行文字和每個token值
	Ogre::Real posX, posY;	// 暫存Graphviz產生的座標位置
	DIVA_VM *tempVM;		// 暫存此次被更動的VM

	// 記錄第一個VM的位置值 
	// 因為Graphviz在計算給定大小的node的座標會過大，但實際上node間的距離不遠且不會overlay
	// 所以使用計算相對位置的方式還原VM間的排列方式
	//
	// 而轉換後的DIVA_VM座標，因為間距過小，所以加入scaleVal以解決overlay的問題
	Ogre::Real offsetX, offsetY, scaleVal = 3;
	bool isFirst = true;
	while(fin.good())
	{
		fin.getline(str_a_line,256);	// 讀一行
		str_line = str_a_line;
		if(str_line.find("vm_node_") != string::npos)
		{
			// 如果是連結關係 則略過此行
			if(str_line.find("->") != string::npos) continue;

			// 依node name反查對應的DIVA_VM *
			tempVM = NULL;
			token = str_line.substr( str_line.find_first_of("vm_node_"), str_line.find(" ")-1);	// 取得vm_node_??
			for(map<DIVA_VM*, string>::iterator it = vm_names.begin();it!=vm_names.end();it++)
			{
				// 找到符合的DIVA_VM*
				if((*it).second.compare(token) == 0)
				{
					tempVM = (it)->first;
					break;
				}
			}

			// 防止找不到VM而造成後面發生null exception
			if(tempVM == NULL) continue;

			// 擷取pos資訊
			// 找到pos="xxx,yyy"] -> xxx,yyy
			// 假設pos是最後一個屬性 而且後面緊接著],   ex. pos="xxx,yyy"]
			// bug: token切出來會是"xxx,yyy"];"
			token = str_line.substr(str_line.find("pos=\"")+5, str_line.find_last_of("\"]")-2);
			posX = Ogre::StringConverter::parseReal(token.substr(0, token.find(",")));
			posY = Ogre::StringConverter::parseReal(token.substr(token.find(",")+1));

			// 只在第一次記錄偏移值
			if(isFirst)
			{
				offsetX = posX - *(tempVM->get_vm_rx());
				offsetY = posY - *(tempVM->get_vm_ry());

				isFirst = false;
			}

			// 計算Graphviz的座標的相對值
			posX -= offsetX;
			posY -= offsetY;

			// 使用scaleVal拉大DIVA_VMs之間的距離
			tempVM->setPosition(posX*scaleVal,posY*scaleVal, *tempVM->get_vm_rz());
		}
		else
		{
			// 不是node資訊，跳過此行
			continue;
		}
	}

	
}